package com.youzhi.backend.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youzhi.backend.dto.LoginResponse;
import com.youzhi.backend.dto.OperateLogTypeEnum;
import com.youzhi.backend.dto.Page;
import com.youzhi.backend.entity.Organization;
import com.youzhi.backend.entity.Role;
import com.youzhi.backend.entity.User;
import com.youzhi.backend.entity.UserRole;
import com.youzhi.backend.exception.EcpEnum;
import com.youzhi.backend.exception.YouZhiException;
import com.youzhi.backend.mapper.UserMapper;
import com.youzhi.backend.request.*;
import com.youzhi.backend.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 雇员表 服务实现类
 * </p>
 *
 * @author xumeng
 * @since 2022-11-18
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService, IPagePostPositionProcessor {


    public static String default_password = "123456";

    /**
     * 管理员用户id
     */
    public static int admin_user_id = 1;


    @Autowired
    private IUserRoleService userRoleService;

    @Autowired
    private IRoleService roleService;


    @Autowired
    private IRolePrivilegeService rolePrivilegeService;


    @Autowired
    private IUserSessionService userSessionService;

    @Autowired
    private IOperateLogService operateLogService;

    @Autowired
    @Lazy
    private IOrganizationService organizationService;


    @Override
    public void addUser(UpdateUserRequest request) {

        User user = new User();
        BeanUtils.copyProperties(request, user);


        //校验用户是否重复
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("name", user.getName());
        long count = count(queryWrapper);
        if (count > 0L) {
            throw YouZhiException.newException(EcpEnum.user_name_repeat, user.getName());
        }


        //没有输密码给一个默认密码
        String password = user.getPassword();
        if (StrUtil.isEmpty(password)) {
            password = default_password;
        }

        user.setOrganizationId(request.getOrganizationId());
        user.setPassword(getEncryptPassword(password));
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());

        //保存用户
        save(user);

        //保存角色
        if (request.getRoleIds() != null) {
            updateUserRole(user.getId(), request.getRoleIds());
        }

        //添加操作日志
        operateLogService.addOperateLog(OperateLogTypeEnum.add, "新增用户 " + user.getName(), user.getId().toString());

    }


    @Override
    public LoginResponse login(LoginRequest request) {

        User user = getUserByName(request.getName());

        //用户不存在
        if(user == null){
            throw  YouZhiException.newException(EcpEnum.user_not_exist);
        }

        //密码正确
        if (user.getPassword().equals(getEncryptPassword(request.getPassword()))) {
            //并且判断当前用户已经登陆中，则踢出登陆中的用户（实现单点登陆功能)
            if (userSessionService.isLogin(request.getName())) {
                //移除用户在线状态，并且发送消息通知
                userSessionService.remove(request.getName());
                //shiro登出
                SecurityUtils.getSubject().logout();
            }
        } else {
            throw YouZhiException.newException(EcpEnum.login_fail);
        }

        //登陆
        userSessionService.login(request.getName(), request.getPassword());

        //登陆成功,返回webSocketId,前端用户用来进行websocket连接
        String webSocketId = bindSessionData(user, request.getClientIp());

        //获取用户所有权限并返回
        Set<String> privileges = getPrivilegeCodesByUserName(request.getName());
        LoginResponse loginResponse = new LoginResponse();
        loginResponse.setWebSocketId(webSocketId);
        loginResponse.setPrivileges(privileges);
        loginResponse.setUserId(user.getId());
        loginResponse.setUserName(user.getName());

        //记录登陆日志
        operateLogService.addOperateLog(OperateLogTypeEnum.login, request.getName() + " 用户登陆", null);

        return loginResponse;


    }

    private String bindSessionData(User user, String clientIp) {
        Serializable sessionId = userSessionService.getCurrentSessionId();
        String webSocketId = UUID.randomUUID().toString();
        Map<String, Object> map = new HashMap<>();
        map.put(IUserSessionService.attr_sessionId, sessionId);
        map.put(IUserSessionService.attr_webSocketId, webSocketId);
        map.put(IUserSessionService.attr_userId, user.getId());
        map.put(IUserSessionService.attr_clientIp, clientIp);
        userSessionService.setCurrentUserAttr(map);
        return webSocketId;
    }


    @Override
    public void logout(LogoutRequest request) {
        //删除用户在线登陆状态
        userSessionService.logout();
    }


    @Override
    public IPage<User> userPage(UserPageRequest request) {

        QueryWrapper queryWrapper = new QueryWrapper();
        if (StrUtil.isNotEmpty(request.getName())) {
            queryWrapper.like(User.Fields.name, request.getName());
        }
        if (StrUtil.isNotEmpty(request.getPhone())) {
            queryWrapper.like(User.Fields.phone, request.getPhone());
        }

        queryWrapper.orderByDesc(User.Fields.createTime);

        return page(Page.ofPage(request), queryWrapper);
    }

    @Override
    public void updateUser(UpdateUserRequest request) {
        User user = new User();
        user.setId(request.getId().longValue());
        user.setUpdateTime(new Date());

        //传password则不会传其他字段,单独的设置密码的快捷方式
        if (request.getPassword() != null) {
            String password = new Sha256Hash(UserServiceImpl.default_password).toHex();
            user.setPassword(password);
            updateById(user);
        } else {
            BeanUtils.copyProperties(request, user);
            updateById(user);
        }

        //更新用户角色
        if (request.getRoleIds() != null) {
            updateUserRole(request.getId(), request.getRoleIds());
        }

        //更新用户的组织id
        if (request.getOrganizationId() != null) {
            Organization organization = organizationService.getByPrincipleId(request.getId());
            //如果用户是另外一个部门的负责人,需要将他从另外部门移除
            if (organization != null &&
                    !organization.getId().equals(request.getOrganizationId())) {
                organizationService.deletePrincipleIdById(organization.getId());
            }
        }

        //添加操作日志
        operateLogService.addOperateLog(OperateLogTypeEnum.edit, "更新用户信息", request.getId().toString());
    }


    private Set<String> getPrivilegeCodesByUserName(String userName) {

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(User.Fields.name, userName);
        User user = getOne(queryWrapper);

        //用户角色
        List<UserRole> userRoles = userRoleService.selectByUserIds(CollUtil.newArrayList(user.getId()));

        if (CollUtil.isEmpty(userRoles)) {
            return new HashSet<>();
        }

        //角色id集合
        List<Long> collect = userRoles.stream().map(userRole -> userRole.getRoleId()).collect(Collectors.toList());

        return rolePrivilegeService.getPrivilegeCodes(collect);
    }


    public void updateUserRole(Long userId, List<Integer> roleIds) {

        if (CollUtil.isEmpty(roleIds)) {
            removeUserRoleByUserId(userId);
        } else {
            updateUserRole0(userId, roleIds);
        }

    }


    /**
     * 通过用户id删除与用户相关的角色
     *
     * @param id
     */
    private void removeUserRoleByUserId(Long id) {
        Map<String, Object> map = new HashMap<>();
        map.put("userId", id);
        userRoleService.removeByMap(map);
    }


    //更新用户角色
    private void updateUserRole0(Long userId, List<Integer> roleIds) {

        //传过来的角色id数量与数据库角色数量不匹配抛出异常
        List<Role> roles = roleService.listByIds(roleIds);
        if (roles.size() != roleIds.size()) {
            throw YouZhiException.newException(EcpEnum.role_id_match_error);
        }

        //删除老的用户角色
        removeUserRoleByUserId(userId);


        //保存新的角色
        List<UserRole> userRoles = roles.stream().map(e -> {
            UserRole userRole = new UserRole();
            userRole.setUserId(userId.longValue());
            userRole.setRoleId(e.getId());
            userRole.setRoleCode(e.getCode());
            userRole.setCreateTime(new Date());
            userRole.setUpdateTime(new Date());
            return userRole;
        }).collect(Collectors.toList());
        userRoleService.saveBatch(userRoles);


    }

    @Override
    public void modifyPassword(ModifyPasswordRequest request) {

        String currentUser = userSessionService.getCurrentUser();

        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(User.Fields.name, currentUser);
        User user = getOne(queryWrapper);

        //两次输入密码不一致
        if (!request.getNewPassword().equals(request.getAffirmedNewPassword())) {
            throw YouZhiException.newException(EcpEnum.passwords_are_different);
        }

        //对比输入密码是否正确
        String oldEncryptPassword = getEncryptPassword(request.getOldPassword());
        if (!oldEncryptPassword.equals(user.getPassword())) {
            throw YouZhiException.newException(EcpEnum.password_error);
        }

        //设置新密码
        user.setPassword(getEncryptPassword(request.getNewPassword()));
        user.setUpdateTime(new Date());
        updateById(user);

        //新密码设置成功，退出登陆要求用户重新登陆
        userSessionService.logout();

    }

    @Override
    public void deleteUsers(List<Long> userIds) {

        List collect = (List) userIds.stream()
                .filter(e -> Integer.parseInt(e.toString()) != admin_user_id)
                .collect(Collectors.toList());


        //批量删除用户
        removeBatchByIds(collect);

        //根据用户id,批量删除用户角色
        userRoleService.deleteByUserIds(collect);

        //添加操作日志
        operateLogService.addOperateLog(OperateLogTypeEnum.delete, "删除用户", CollUtil.join(collect, ","));

    }

    @Override
    public IPage<Map<String, Object>> process(IPage<Map<String, Object>> page) {

        //添加用户角色相关字段
        addRoleInfo(page);

        //添加用户部门相关字段
        addOrganizationInfo(page);

        return page;
    }


    /**
     * 添加用户部门相关字段
     *
     * @param page
     * @return
     */
    private IPage<Map<String, Object>> addOrganizationInfo(IPage<Map<String, Object>> page) {

        Set<Long> organizationIds = page.getRecords().stream().filter(e -> e.get("organizationId") != null).map(e -> Long.parseLong(e.get("organizationId").toString())).collect(Collectors.toSet());

        List<Organization> organizations = organizationService.listByIds(organizationIds);

        page.getRecords().stream().filter(e -> e.get("organizationId") != null).forEach(user -> {
            for (Organization organization : organizations) {
                if (user.get("organizationId").equals(organization.getId())) {
                    user.put("organizationName", organization.getName());
                }
            }

        });

        return page;
    }


    /**
     * 添加用户角色相关字段
     *
     * @param page
     * @return
     */
    private IPage<Map<String, Object>> addRoleInfo(IPage<Map<String, Object>> page) {


        List<Object> userIds = page.getRecords().stream().map(e -> e.get("id")).collect(Collectors.toList());
        //查询用户角色
        List<UserRole> userRoles = userRoleService.selectByUserIds(userIds);

        //查询角色
        Set<Long> roleIds = userRoles.stream().map(e -> e.getRoleId()).collect(Collectors.toSet());

        List<Role> roles = CollUtil.isNotEmpty(roleIds) ? roleService.listByIds(roleIds) : new ArrayList<>();

        Map<Long, List<UserRole>> groupMap = userRoles.stream().collect(Collectors.groupingBy(UserRole::getUserId));


        page.getRecords().stream().forEach(user -> {

            List<UserRole> userRoleList = groupMap.get(user.get("id"));

            if (userRoleList != null) {
                List<Long> roleIdsOfUser = userRoleList.stream().map(userRole -> userRole.getRoleId()).collect(Collectors.toList());
                user.put("roleIds", CollUtil.join(roleIdsOfUser, ","));

                String roleNames = roles.stream().filter(e -> roleIdsOfUser.contains(e.getId())).
                        map(e2 -> e2.getName()).collect(Collectors.joining(","));
                user.put("roleNames", roleNames);
            }

        });

        return page;
    }


    /**
     * 获取加密后的密码
     *
     * @param password
     * @return
     */
    private String getEncryptPassword(String password) {
        return new Sha256Hash(password).toHex();
    }


    /**
     * 通过用户名查询用户信息
     *
     * @param userName
     * @return
     */
    public User getUserByName(String userName) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq(User.Fields.name, userName);
        return getOne(wrapper);
    }


    @Override
    public List<Map<String, Object>> queryUserName(String name) {
        QueryWrapper<User> queryWrapper = new QueryWrapper();
        queryWrapper.select(User.Fields.id, User.Fields.name);
        queryWrapper.like(User.Fields.name, name);
        List<User> list = list(queryWrapper);
        if (CollUtil.isNotEmpty(list)) {
            return list.stream().map(e -> {
                Map<String, Object> map = new HashMap<>();
                map.put("value", e.getName());
                map.put("id", e.getId());
                return map;
            }).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }


    @Override
    public void updateUserOrganizationId(Long organizationId, Long userId) {

        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();

        updateWrapper.set(User.Fields.organizationId, organizationId);

        updateWrapper.eq(User.Fields.id, userId);

        update(updateWrapper);

    }


    public void removeOrganizationId(Long organizationId) {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set(User.Fields.organizationId, null);
        updateWrapper.eq(User.Fields.organizationId, organizationId);
        update(updateWrapper);
    }


    @Override
    public void removeUserOrganization(Long id) {

        //将用户表 组织机构id移除
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set(User.Fields.organizationId, null);
        updateWrapper.eq(User.Fields.id, id);
        update(updateWrapper);

        //将组织架构表 用户移除
        organizationService.deletePrincipleId(id);

        //添加操作日志
        operateLogService.addOperateLog(OperateLogTypeEnum.delete, "将用户移除组织架构", id.toString());

    }

    @Override
    public void addUserOrganization(Long organizationId, String name) {

        User user = getUserByName(name);

        if (user == null) {
            throw YouZhiException.newException(EcpEnum.user_not_exist);
        }

        //将组织架构表 用户移除
        organizationService.deletePrincipleId(user.getId());

        //设置用户新的组织部门信息
        user.setOrganizationId(organizationId);
        user.setUpdateTime(new Date());
        updateById(user);

        //添加操作日志
        operateLogService.addOperateLog(OperateLogTypeEnum.add, "将" + name + "加入组织架构", organizationId.toString());
    }


}
